%locations
%{
#include "lex.yy.c"
#include "treeNode.h"
extern int yylineno;
extern int last_error_line;
#define YYDEBUG 0
tree_t mTreeRoot;
int syntaxError = 0;
int syntaxLastErrorLine = 0;
void yyerror(const char *s);
void syntaxPrint(const char *format, ...){
#ifdef LOCAL
	fprintf(stderr, "syntaxPrint: Line %d:\n\t", yylloc.first_line);
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
#endif
}
tree_node_t *generate_syn_node(
	char *name, 
	tree_node_t *son, node_data_t *pdata, 
	int line_index, int var_length, ...);
%}
/* declared yylval types */
%union{
	tree_node_t * node;
}

%token <node>INT FLOAT ID
%token <node>SEMI COMMA 
%token <node>ASSIGNOP
%token <node>OR AND
%token <node>RELOP
%token <node>PLUS MINUS STAR DIV
%token <node>DOT
%token <node>NOT
%token <node>TYPE
%token <node>LP RP LB RB 
%token <node>LC RC
%token <node>STRUCT
%token <node>RETURN
%token <node>IF ELSE
%token <node>WHILE



/* define type  */
%type <node>  Program
%type <node>  ExtDefList
%type <node>  ExtDef
%type <node>  ExtDecList
%type <node>  Specifier
%type <node>  StructSpecifier
%type <node>  OptTag
%type <node>  Tag
%type <node>  VarDec
%type <node>  FunDec
%type <node>  VarList
%type <node>  ParamDec
%type <node>  CompSt
%type <node>  StmtList
%type <node>  Stmt
%type <node>  DefList
%type <node>  Def
%type <node>  DecList
%type <node>  Dec
%type <node>  Exp
%type <node>  Args


// 优先级排序
%start 		Program
%right 		ASSIGNOP
%left 		OR
%left 		AND
%left 		RELOP
%left 		PLUS MINUS
%left 		STAR DIV
%right 		NOT
%left 		DOT
%left 		LB RB  // 圆括号
%left 		LP RP  // 方括号
%nonassoc 	LOWER_THAN_ELSE
%nonassoc 	ELSE

%%
// High-level definitions
Program: 		ExtDefList 									{	$$ = generate_syn_node("Program\0", $1, NULL,  @$.first_line, 0, NULL);
																mTreeRoot = $$;
															}
	;				
ExtDefList: 	ExtDef ExtDefList 							{	$$ = generate_syn_node("ExtDefList\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			/* Empty */ 								{	$$ = NULL;}
	;				
ExtDef: 		Specifier ExtDecList SEMI					{	$$ = generate_syn_node("ExtDef\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Specifier SEMI								{	$$ = generate_syn_node("ExtDef\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			Specifier FunDec CompSt						{	$$ = generate_syn_node("ExtDef\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Specifier FunDec SEMI						{	$$ = generate_syn_node("ExtDef\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			error SEMI 									{ ; }
	| 			Specifier error SEMI 						{ ; }
	| 			error Specifier SEMI 						{ ; }
	;				
ExtDecList: 	VarDec 										{	$$ = generate_syn_node("ExtDecList\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			VarDec COMMA ExtDecList						{	$$ = generate_syn_node("ExtDecList\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			VarDec error ExtDecList 					{ ; }
	| 			error										{ ; }
	;				
// Specifier: TYPE				
Specifier: 		TYPE										{	$$ = generate_syn_node("Specifier\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			StructSpecifier								{	$$ = generate_syn_node("Specifier\0", $1, NULL,  @$.first_line, 0, NULL);}
	|			error										{ ; }
	;				
StructSpecifier:STRUCT OptTag LC DefList RC					{	$$ = generate_syn_node("StructSpecifier\0", $1, NULL,  @$.first_line, 4, $2, $3, $4, $5, NULL);}
	| 			STRUCT Tag									{	$$ = generate_syn_node("StructSpecifier\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	;				
OptTag: 		ID											{	$$ = generate_syn_node("OptTag\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			/*Empty*/ 									{	$$ = NULL;}
	;				
Tag: 			ID											{	$$ = generate_syn_node("Tag\0", $1, NULL,  @$.first_line, 0, NULL);}
	;				
// Declarators				
VarDec: 		ID											{	$$ = generate_syn_node("VarDec\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			VarDec LB INT RB							{	$$ = generate_syn_node("VarDec\0", $1, NULL,  @$.first_line, 3, $2, $3, $4, NULL);}
	|			error RB { ; }
	| 			VarDec LB error RB 							{ ; }
	;				
FunDec: 		ID LP VarList RP							{	$$ = generate_syn_node("FunDec\0", $1, NULL,  @$.first_line, 3, $2, $3, $4, NULL);}
	| 			ID LP RP									{	$$ = generate_syn_node("FunDec\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	|			error RP { ; }
	| 			ID LP error RP                              { ; }
	| 			error LP VarList RP							{ ; }
	;				
VarList: 		ParamDec COMMA VarList						{	$$ = generate_syn_node("VarList\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			ParamDec									{	$$ = generate_syn_node("VarList\0", $1, NULL,  @$.first_line, 0, NULL);}
	|			error										{ ; }
	;				
ParamDec: 		Specifier VarDec							{	$$ = generate_syn_node("ParamDec\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	;				
// Statements				
CompSt: 		LC DefList StmtList RC						{	$$ = generate_syn_node("CompSt\0", $1, NULL,  @$.first_line, 3, $2, $3, $4, NULL);}
	| 			error RC { ; }
	;				
StmtList: 		Stmt StmtList								{	$$ = generate_syn_node("StmtList\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			/* Empty */ 								{	$$ = NULL;}
	;				
Stmt: 			Exp SEMI									{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			CompSt										{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 0, NULL);	}
	| 			RETURN Exp SEMI								{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			IF LP Exp RP Stmt %prec LOWER_THAN_ELSE		{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 4, $2, $3, $4, $5, NULL);}		
	|			IF LP Exp RP Stmt ELSE Stmt					{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 6, $2, $3, $4, $5, $6, $7, NULL);}
	| 			WHILE LP Exp RP Stmt						{	$$ = generate_syn_node("Stmt\0", $1, NULL,  @$.first_line, 4, $2, $3, $4, $5, NULL);}
	| 			IF error Exp RP Stmt { ; }
	| 			IF LP error RP Stmt { ; }
	| 			IF LP Exp error Stmt { ; }
	| 			WHILE error Exp RP Stmt { ; }
	| 			WHILE LP error RP Stmt { ; }
	| 			WHILE LP Exp error Stmt { ; }
	| 			error SEMI { ; }
	| 			Exp error SEMI { ; }
	| 			Exp error { ; }
	| 			RETURN error SEMI { ; }
	| 			RETURN Exp error { ; }
	;			
// Local Definitions				
DefList: 		Def DefList									{	$$ = generate_syn_node("DefList\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			/*Empty*/									{	$$ = NULL;}
	;				
Def: 			Specifier DecList SEMI						{	$$ = generate_syn_node("Def\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Specifier error SEMI                        {   ; }
	|			Specifier DecList error						{	; }
	;				
DecList:	 	Dec											{	$$ = generate_syn_node("DecList\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			Dec COMMA DecList							{	$$ = generate_syn_node("DecList\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	;				
Dec: 			VarDec										{	$$ = generate_syn_node("Dec\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			VarDec ASSIGNOP Exp							{	$$ = generate_syn_node("Dec\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	;				
// Expressions				
Exp: 			Exp ASSIGNOP Exp							{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp AND Exp									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp OR Exp									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp RELOP Exp								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp PLUS Exp								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp MINUS Exp								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp STAR Exp								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp DIV Exp									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			LP Exp RP									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			MINUS Exp									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			NOT Exp										{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 1, $2, NULL);}
	| 			ID LP Args RP								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 3, $2, $3, $4, NULL);}
	| 			ID LP RP									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp LB Exp RB								{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 3, $2, $3, $4, NULL);}
	| 			Exp DOT ID 									{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);
															}// 这里应该只是不符合语义分析，但是还是符合语法分析的
	| 			ID											{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			INT											{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 0, NULL);}
	| 			FLOAT										{	$$ = generate_syn_node("Exp\0", $1, NULL,  @$.first_line, 0, NULL);}
// 防止错误字	
	| LP error RP { ; }
	| ID LP error RP {; }
	| Exp LB error RB{ ; }
	| MINUS error { ; }
	| NOT error { ; }
	| Exp STAR error { ; }
	| error STAR { ; }
	| Exp DIV error { ; }
	| error DIV { ; }
	| Exp PLUS error { ; }
	| Exp MINUS error { ; }
	| Exp RELOP error { ; }
	| Exp AND error { ; }
	| Exp OR error { ; }
	| Exp ASSIGNOP error { ; }
	| 			error										{ ; }
	;				
Args: 			Exp COMMA Args								{	$$ = generate_syn_node("Args\0", $1, NULL,  @$.first_line, 2, $2, $3, NULL);}
	| 			Exp											{	$$ = generate_syn_node("Args\0", $1, NULL,  @$.first_line, 0, NULL);}
	;
%%
void yyerror(const char *s) {
	syntaxError += 1;
	if(last_error_line == yylineno){
		return;
	}
    printf("Error type B at Line %d: %s.\n", yylineno, s);
	last_error_line = syntaxLastErrorLine = yylineno;


}
tree_node_t *generate_syn_node(
	char *name, 
	tree_node_t *son, node_data_t * pdata,
	int line_index, int var_length, ...)
{
	node_data_t data = {
		.data = NULL,		
		.data_type = DEFAULT_DATA
	};
	if (pdata != NULL) {
		syntaxPrint("pdata is not NULL");
	}
	tree_node_t *node = create_and_init_node(
		name, data, 
		SYNTAX_NODE, 
		line_index
	);
	node->son = son;

	if(son == NULL){
		syntaxPrint("son is NULL");
		return node;
	}

// va_list 参考 腾讯云开发者社区 
// https://cloud.tencent.com/developer/article/2398146
	va_list args;
	va_start(args, var_length);

	tree_node_t *current = son;
	tree_node_t *next;
	for (int i = 0; i < var_length; i++) {
		next = va_arg(args, tree_node_t*);
		current->sibling = next;
		if(next != NULL){
			current = next;
		}
		// 可能中间的参数是NULL，但是NULL之后还有东西
	}

	current->sibling = NULL; // 结束链表
	va_end(args);

	return node;
}

